/**
 * Software License, Version 1.0
 * 
 * Copyright 2008 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgment:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgment may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or Granules, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called Granules, 
 * nor may Indiana University or Community Grids Lab or Granules appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 * LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 * WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 * NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 * INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 * INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 * "VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 * LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 * ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 * GENERATED USING SOFTWARE.
 */
package cgl.granules.operation;

import java.util.Enumeration;
import java.util.concurrent.ConcurrentHashMap;

import cgl.granules.scheduler.TaskLifecycleMetrics;

/**
 * An implementation of the <code>ProgressTracker</code> interface.
 * 
 * @author Shrideep Pallickara
 * 
 */

public class ProgressTrackerWidget implements ProgressTracker {

    private String jobIdentifier;
    private String trackerBinding;
    private transient int numOfDeployments;
    private ConcurrentHashMap<String, TaskLifecycleMetrics> statusOfTasks;
    private ConcurrentHashMap<String, String> deploymentStreams;
    private JobLifecycleObserver jobLifecycleObserver;


    protected ProgressTrackerWidget(String binding, String jobIdentifier,
            JobLifecycleObserver jobLifecycleObserver) {
        this.trackerBinding = binding;
        this.jobIdentifier = jobIdentifier;
        this.jobLifecycleObserver = jobLifecycleObserver;
        statusOfTasks = new ConcurrentHashMap<String, TaskLifecycleMetrics>();
        deploymentStreams = new ConcurrentHashMap<String, String>();
    }


    @Override
    public int getCompletionStatus(String taskIdentifier) {
        int completionStatus = TaskLifecycleMetrics.NOT_SET;

        /** Initialize to UNKNOWN */
        if (!statusOfTasks.containsKey(taskIdentifier)) {
            return completionStatus;
        }

        completionStatus =
            statusOfTasks.get(taskIdentifier).getExecutionStatus();
        return completionStatus;
    }


    @Override
    public String getIdentifier() {
        return jobIdentifier;
    }


    /**
     * Updates the task execution status associated with a given task.
     * 
     * @param binding
     *            The binding for making changes
     * @param taskIdentifier
     *            The identifier of the task whose status is being updated
     * @param taskLifecycleMetrics
     *            The lifecycle metrics for the task in question.
     */
    protected void updateTaskExecutionStatus(
                                             String binding,
                                             String taskIdentifier,
                                             TaskLifecycleMetrics taskLifecycleMetrics,
                                             String taskDeploymentStream) {

        if (!passBindingCheck(binding)) {
            System.out.println("Failed binding check ...");
            return;
        }

        boolean reportOnNewTask = false;
        if (!statusOfTasks.containsKey(taskIdentifier)) {
            reportOnNewTask = true;
        }

        statusOfTasks.put(taskIdentifier, taskLifecycleMetrics);
        deploymentStreams.put(taskIdentifier, taskDeploymentStream);

        if (reportOnNewTask) {
            jobLifecycleObserver.startedExecution(jobIdentifier, taskIdentifier);
        }

        if (isTaskComplete(taskLifecycleMetrics)) {
            jobLifecycleObserver.completedExecution(jobIdentifier,
                                                    taskIdentifier,
                                                    taskLifecycleMetrics);
        }

        if (isComplete()) {
            if (isSuccessful()) {
                jobLifecycleObserver.completedSuccessfully(jobIdentifier, this);
            } else {
                jobLifecycleObserver.completedWithErrors(jobIdentifier, this);
            }
        }
    }


    private boolean isTaskComplete(TaskLifecycleMetrics taskLifecycleMetrics) {
        boolean completed = false;
        int completionStatus = taskLifecycleMetrics.getExecutionStatus();

        if (completionStatus == TaskLifecycleMetrics.COMPLETE
            || completionStatus == TaskLifecycleMetrics.ABNORMAL_TERMINATION
            || completionStatus == TaskLifecycleMetrics.ABORTED) {
            completed = true;
        }
        return completed;
    }


    @Override
    public TaskLifecycleMetrics getTaskLifecycleMetrics(String taskIdentifier) {
        return statusOfTasks.get(taskIdentifier);
    }


    @Override
    public String getDeploymentStream(String taskIdentifier) {
        return deploymentStreams.get(taskIdentifier);
    }


    @Override
    public int getNumberOfConcurrentDeployments() {
        return numOfDeployments;
    }


    @Override
    public Enumeration<String> getTaskIdentifiers() {
        return statusOfTasks.keys();
    }


    @Override
    public boolean isComplete() {
        boolean completed = false;
        Enumeration<TaskLifecycleMetrics> e = statusOfTasks.elements();

        int numOfReportingTasks = 0;
        while (e.hasMoreElements()) {
            TaskLifecycleMetrics taskLifecycleMetrics = e.nextElement();
            if (!isTaskComplete(taskLifecycleMetrics)) {
                return completed;
            }
            numOfReportingTasks++;
        }

        if (numOfReportingTasks < numOfDeployments) {
            return completed;
        }

        completed = true;
        return completed;
    }


    @Override
    public boolean isSuccessful() {
        boolean completed = false;
        Enumeration<TaskLifecycleMetrics> e = statusOfTasks.elements();

        int numOfReportingTasks = 0;
        while (e.hasMoreElements()) {
            TaskLifecycleMetrics lifecycleMetrics = e.nextElement();
            int completionStatus = lifecycleMetrics.getExecutionStatus();
            if (completionStatus != TaskLifecycleMetrics.COMPLETE) {
                return completed;
            }
            numOfReportingTasks++;
        }

        if (numOfReportingTasks < numOfDeployments) {
            return completed;
        }

        completed = true;
        return completed;
    }


    /**
     * Does a check to see if the binding that has been passed, matches the one
     * that has been established.
     * 
     * @param binding
     * @return
     */
    private boolean passBindingCheck(String binding) {
        if (binding == null) {
            return false;
        }

        if (binding.equals(trackerBinding)) {
            return true;
        }
        return false;
    }


    protected void incrementNumberOfDeployments() {
        numOfDeployments++;
    }


    @Override
    public String toString() {
        StringBuffer strBuf = new StringBuffer();
        strBuf.append("\n\n");
        if (isComplete() && isSuccessful()) {
            strBuf.append("Job [" + jobIdentifier
                + "] completed SUCCESSfully.\n");
        }

        if (isComplete() && !isSuccessful()) {
            strBuf.append("Job [" + jobIdentifier
                + "] completed with FAILURES.\n");
        }

        if (!isComplete()) {
            strBuf.append("Job [" + jobIdentifier + "] is still EXECUTING. \n");
        }
        strBuf.append("This Job comprises (" + numOfDeployments);
        strBuf.append(") tasks. Their status is enumerated below:\n\n");

        Enumeration<String> taskIdentifiers = statusOfTasks.keys();

        int counter = 0;
        while (taskIdentifiers.hasMoreElements()) {
            String taskId = taskIdentifiers.nextElement();
            counter++;
            strBuf.append("(" + counter + ") ");
            strBuf.append("Task [" + taskId + "] -> \n");
            strBuf.append(getTaskLifecycleMetrics(taskId).toString());
            strBuf.append("\n\n");
        }
        return strBuf.toString();
    }
}
